package com.olive.jwt;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;


@Component
public class CertificateProvider {

	@Autowired
	private CertificateConfig certificateConfig;

	private RSAPrivateKey rsaPrivateKey = null;

	private Map<String, RSASSAVerifier> verifiers = new HashMap<>();

	@PostConstruct
	public void init() {
		rsaPrivateKey = this.getPrivateKey();
	}

	public RSAPrivateKey getPrivateKey() {
		if (rsaPrivateKey != null) {
			return rsaPrivateKey;
		}
		String use = certificateConfig.getUseKid();
		if (StringUtils.isEmpty(use)) {
			System.out.println("certificate kid is required");
			return null;
		}
		List<CertVO> certVOs = certificateConfig.getCertificates();
		if (certVOs==null && certVOs.size()==0) {
			System.out.println("certificate is required");
			return null;
		}
		try {
			for (CertVO certVO : certVOs) {
				if (use.equals(certVO.getKid())) {
					// 加载私钥
					rsaPrivateKey = this.loadRSARSAPrivateKey(certVO.getPrivateKey());
				}
				RSAPublicKey publicKey = loadRSAPublicKey(certVO.getPublicKey());
				verifiers.put(certVO.getKid(), new RSASSAVerifier(publicKey));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (rsaPrivateKey != null) {
			return rsaPrivateKey;
		} else {
			System.out.println("getPrivateKey certificate kid is required，certificate kid is required");
			return null;
		}
	}

	/**
	 * 加载公钥
	 *
	 * @param keyStr
	 *            公钥字符串
	 * @return 公钥实体
	 * @throws NoSuchAlgorithmException
	 *             KeyFactory中无该算法实现
	 * @throws InvalidKeySpecException
	 *             密钥无法识别
	 */
	private RSAPublicKey loadRSAPublicKey(String keyStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
		byte[] clear = publicKeyStrToBytes(keyStr);
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(clear);
		KeyFactory fact = KeyFactory.getInstance("RSA");
		return (RSAPublicKey) fact.generatePublic(keySpec);
	}

	private RSAPrivateKey loadRSARSAPrivateKey(String keyStr) throws Exception {
		String begin = "-----BEGIN PRIVATE KEY-----";
		String end = "-----END PRIVATE KEY-----";
		String key = keyStr.replace(begin, "").replace(end, "").replaceAll("\\s", "");
		PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
		KeyFactory kf = KeyFactory.getInstance("RSA");
		return (RSAPrivateKey) kf.generatePrivate(spec);
	}

	/**
	 * 公钥 字符串转换成二进制
	 *
	 * @param keyStr
	 *            密钥字符串
	 * @return 密钥/公钥 二进制
	 */
	private byte[] publicKeyStrToBytes(String keyStr) {
		String begin = "-----BEGIN PUBLIC KEY-----";
		String end = "-----END PUBLIC KEY-----";
		String key = keyStr.replace(begin, "").replace(end, "").replaceAll("\\s", "");
		return Base64.getDecoder().decode(key);
	}

	/**
	 * 
	 * @param jwtPayloadVO
	 * @return
	 */
	public String generateAccessToken(JwtPayloadVO jwtPayloadVO) {
		Map<String, Object> playloadMap = new HashMap<>();
		playloadMap.put("jti", jwtPayloadVO.getJti());
		playloadMap.put("tid", jwtPayloadVO.getTid());
		playloadMap.put("cid", jwtPayloadVO.getCid());
		playloadMap.put("iss", jwtPayloadVO.getIss());
		playloadMap.put("sub", jwtPayloadVO.getSub());
		playloadMap.put("exp", jwtPayloadVO.getExp());
		playloadMap.put("iat", jwtPayloadVO.getIat());
		if (StringUtils.hasLength(jwtPayloadVO.getUid())) {
			playloadMap.put("uid", jwtPayloadVO.getUid());
		}
		try {
			// 创建JWS头，设置签名算法和类型
			JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(certificateConfig.getUseKid())
					.type(JOSEObjectType.JWT).build();
			JWTClaimsSet claimsSet = JWTClaimsSet.parse(playloadMap);
			// 创建RSA签名器
			JWSSigner signer = new RSASSASigner(rsaPrivateKey, true);
			SignedJWT signedJWT = new SignedJWT(header, claimsSet);
			signedJWT.sign(signer);
			return signedJWT.serialize();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 
	 * @param token
	 * @return
	 * @throws ParseException
	 * @throws JOSEException
	 */
	public JWTClaimsSet verify(String token) throws ParseException, JOSEException {
		SignedJWT jwt = SignedJWT.parse(token);
		JWSHeader jwtHeader = jwt.getHeader();
		String keyID = jwtHeader.getKeyID();
		RSASSAVerifier verifier = verifiers.get(keyID);
		if (verifier == null) {
			System.out.println("jwt verify error: kid " + keyID + " mismatch RSASSAVerifier");
			return null;
		}
		boolean verify = jwt.verify(verifier);
		if (!verify) {
			System.out.println("jwt verify fail, invalid token: " + token);
			return null;
		}
		return jwt.getJWTClaimsSet();
	}

}